﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.PeInspector.Reflection.Disassembly;
using Kokomo.Lexical.Coding;
using System.Diagnostics.SymbolStore;
using System.Reflection.Emit;
using Kokomo.Mirror.CliMetadata.DebugInfo;

namespace Kokomo.PeInspector.Reflection
{
	class SourceInfoCollector
	{
		public TextCodeWriter TextWriter { get; private set; }
		public CodeDocumentBuilder DocBuilder { get; private set; }

		private SymDocumentBuilder _symbolDocumentBuilder;
		private SymMethodBuilder _symbolMethodBuilder;

		private MethodBase _method;
		private MethodDecompilation _disassembly;
		private Statement _statement;
		private MsilFrameInfo _frameInfo;
		private MethodBuilder _methodBuilder;
		private ILGenerator _ilgen;

		private int _statementStartCilOffset;
		private int _startLine;
		private int _startColumn;
		private int _endLine;
		private int _endColumn;

		public SourceInfoCollector(
			TextCodeWriter textWriter,
			CodeDocumentBuilder docBuilder,
			SymDocumentBuilder symbolDocumentBuilder
			)
		{
			if (textWriter == null) throw new ArgumentNullException("textWriter");
			if (docBuilder == null) throw new ArgumentNullException("docBuilder");
			if (symbolDocumentBuilder == null) throw new ArgumentNullException("symbolDocumentBuilder");

			this.TextWriter = textWriter;
			this.DocBuilder = docBuilder;
			this._symbolDocumentBuilder = symbolDocumentBuilder;

			docBuilder.MethodBodyStart += new EventHandler<MethodBodyEventArgs>(DocBuilder_MethodBodyStart);
			docBuilder.MethodBodyEnd += new EventHandler<MethodBodyEventArgs>(DocBuilder_MethodBodyEnd);
			docBuilder.StatementStart += new EventHandler<StatementEventArgs>(DocBuilder_StatementStart);
			docBuilder.StatementHeaderEnd += new EventHandler<StatementEventArgs>(DocBuilder_StatementHeaderEnd);
			docBuilder.StatementEnd += new EventHandler<StatementEventArgs>(DocBuilder_StatementEnd);
		}

		void DocBuilder_MethodBodyStart(object sender, MethodBodyEventArgs e)
		{
			this._method = e.Method;
			this._disassembly = e.Disassembly;

			MethodBody methodBody = e.MethodBody;
			this._symbolMethodBuilder = this._symbolDocumentBuilder.DefineMethod(e.Method.MetadataToken, methodBody.GetILAsByteArray().Length);

			//this._methodBuilder = this.MethodBuilderDelegate(e.Method);
			//this._ilgen = this._methodBuilder.GetILGenerator();
		}

		void DocBuilder_StatementStart(object sender, StatementEventArgs e)
		{
			if (this._statement != null)
			{
			}

			MsilFrameInfo frameInfo;
			Statement statement = e.Statement;
			if (this._disassembly.FrameInfo.TryGetValue(statement, out frameInfo))
			{
				if (frameInfo.EndOffset > frameInfo.StartOffset)
				{
					this._statement = e.Statement;
					this._frameInfo = frameInfo;

					this._statementStartCilOffset = frameInfo.StartOffset;
					this._startLine = this.TextWriter.CurrentLineIndex + 1;
					this._startColumn = this.TextWriter.CurrentCharacterIndex;
				}
			}
		}

		void DocBuilder_StatementHeaderEnd(object sender, StatementEventArgs e)
		{
			if (this._statement != null)
			{
				this._symbolMethodBuilder.DefineSequencePoint(
					this._statementStartCilOffset,
					this._startLine,
					this._startColumn,
					this._endLine,
					this._endColumn
					);

				this._statement = null;
				this._frameInfo = null;
			}
			else
			{
			}
		}

		void DocBuilder_StatementEnd(object sender, StatementEventArgs e)
		{
			this.DocBuilder_StatementHeaderEnd(sender, e);
		}

		void DocBuilder_MethodBodyEnd(object sender, MethodBodyEventArgs e)
		{
			this._method = null;
			this._disassembly = null;
			this._symbolMethodBuilder = null;
		}
	}
}
